package org.mybatis.generator.internal;

import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.InnerClass;
import org.mybatis.generator.api.dom.java.InnerEnum;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.internal.util.StringUtility;
import org.springframework.util.ObjectUtils;

import java.text.SimpleDateFormat;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;

/**
 * mybatis逆向工程自定义注释.
 * 2019/5/15
 *
 * @author machuanpeng
 */
public class DefaultCommentGenerator implements CommentGenerator {

    private static String now = new SimpleDateFormat("yyyy-MM-dd").format(new Date());

    private Properties properties;

    private boolean suppressDate;

    private boolean suppressAllComments;

    private boolean addRemarkComments;

    public DefaultCommentGenerator() {
        super();
        properties = new Properties();
        suppressDate = false;
        suppressAllComments = false;
        addRemarkComments = false;
    }

    @Override
    public void addConfigurationProperties(Properties properties) {
        // 获取自定义的 properties
        this.properties.putAll(properties);
    }

    /**
     * 2018/7/19
     * 数据库对应Model类名前加自定义注释
     *
     * @param topLevelClass
     * @param introspectedTable
     * @return void
     * @author machuanpeng
     */
    @Override
    public void addModelClassComment(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        String author = properties.getProperty("author");
        topLevelClass.addJavaDocLine("/**");
        //数据库表备注或数据库表名称
        String remark = introspectedTable.getRemarks();
        String tableName = introspectedTable.getFullyQualifiedTable().getIntrospectedTableName();
        String remarkOrTableName = ObjectUtils.isEmpty(remark)  ? tableName : remark;
        topLevelClass.addJavaDocLine(" * " + remarkOrTableName + ". ");
        topLevelClass.addJavaDocLine(" * " + now);
        topLevelClass.addJavaDocLine(" * ");
        topLevelClass.addJavaDocLine(" * @author " + author);
        topLevelClass.addJavaDocLine(" */");
    }

    /**
     * 2018/7/19
     * 数据库对应Model类中字段注释
     *
     * @param field
     * @param introspectedTable
     * @param introspectedColumn
     * @return void
     * @author machuanpeng
     */
    @Override
    public void addFieldComment(Field field, IntrospectedTable introspectedTable,
                                IntrospectedColumn introspectedColumn) {
        //数据库字段注释
        String remark = introspectedColumn.getRemarks();
        //数据库字段默认值
        String defaultValue = introspectedColumn.getDefaultValue();
        if (Objects.isNull(defaultValue)) {
            defaultValue = "无默认值";
        } else if (ObjectUtils.isEmpty(defaultValue)) {
            defaultValue = "默认值为: ''";
        } else {
            defaultValue = "默认值为: " + defaultValue;
        }
        field.addJavaDocLine("/**");
        if (!ObjectUtils.isEmpty(remark)) {
            field.addJavaDocLine(" * " + remark + " "+defaultValue+". ");
        } else {
            field.addJavaDocLine(" * " + field.getName() + " "+defaultValue+". ");
        }
        /* List<IntrospectedColumn> primaryKey =
                Optional.ofNullable(introspectedTable.getPrimaryKeyColumns()).orElse(new ArrayList<>());
         for (IntrospectedColumn pk : primaryKey) {
            if (Objects.equals(introspectedColumn.getActualColumnName(), pk.getActualColumnName())) {
                //属性是否为主健
                field.addJavaDocLine(" * 主键");
                //主健属性上无需生明可选项跟必填项介绍
                continue;
            }
            if (StringUtility.stringHasValue(remark)) {
                //数据字段是否为必填项
                String must = introspectedColumn.isNullable() ? "可选项" : "必填项";
                field.addJavaDocLine(" * " + must);
            }
        }*/
        field.addJavaDocLine(" */");
    }

    /**
     * 2018/7/19
     * getter方法注释
     *
     * @param method
     * @param introspectedTable
     * @param introspectedColumn
     * @return void
     * @author machuanpeng
     */
    @Override
    public void addGetterComment(Method method, IntrospectedTable introspectedTable,
                                 IntrospectedColumn introspectedColumn) {
    }

    /**
     * 2018/7/19
     * setter方法注释
     *
     * @param method
     * @param introspectedTable
     * @param introspectedColumn
     * @return void
     * @author machuanpeng
     */
    @Override
    public void addSetterComment(Method method, IntrospectedTable introspectedTable,
                                 IntrospectedColumn introspectedColumn) {
//        if (suppressAllComments) {
//            return;
//        }
//        if (method.isConstructor()) {
//            return;
//        }
//
//        String desc = method.getName();
//        String remark = introspectedColumn.getRemarks();
//        if (!ObjectUtils.isEmpty(remark)) {
//            desc = "设置" + remark;
//        }
//
//        String author = properties.getProperty("author");
//        method.addJavaDocLine("/**");
//        method.addJavaDocLine(" * " + desc + ". ");
//        method.addJavaDocLine(" * " + now);
//        method.addJavaDocLine(" * ");
//        List<Parameter> parameters = Optional.ofNullable(method.getParameters()).orElse(new ArrayList<>());
//        parameters.stream().forEach(
//                parameter -> method.addJavaDocLine(" * @param " + parameter.getName() + " " + parameter.getName()));
//        if (Objects.nonNull(method.getReturnType())) {
//            method.addJavaDocLine(" * @return " + method.getReturnType());
//        }
//        method.addJavaDocLine(" * @author " + author);
//        method.addJavaDocLine(" */");
    }

    /**
     * 2018/7/19
     * 方法注释
     *
     * @param method
     * @param introspectedTable
     * @author machuanpeng
     */
    @Override
    public void addGeneralMethodComment(Method method, IntrospectedTable introspectedTable) {

        if (suppressAllComments) {
            return;
        }

        String methodName = method.getName();
        String desc = this.getDesc(methodName);
        List<Parameter> parameterList = method.getParameters();
        if (!ObjectUtils.isEmpty(parameterList.isEmpty())) {
            if ("or".equals(methodName)) {
                desc = "增加或者的查询条件,用于构建或者查询";
            }
        } else {
            if ("or".equals(methodName)) {
                desc = "创建一个新的或者查询条件";
            }
        }

        if (method.isConstructor()) {
            desc = desc + "构造函数";
        }

        String author = properties.getProperty("author");
        method.addJavaDocLine("/**");
        method.addJavaDocLine(" * " + desc + ". ");
        method.addJavaDocLine(" * " + now);
        method.addJavaDocLine(" * ");
        List<Parameter> parameters = Optional.ofNullable(method.getParameters()).orElse(new ArrayList<>());
        parameters.stream().forEach(
                parameter -> method.addJavaDocLine(" * @param " + parameter.getName() + " " + parameter.getName()));
        if (Objects.nonNull(method.getReturnType())) {
            method.addJavaDocLine(" * @return " + method.getReturnType());
        }
        method.addJavaDocLine(" * @author " + author);
        method.addJavaDocLine(" */");
    }

    /**
     * 2018/7/19
     * xml注释
     *
     * @param xmlElement
     * @return void
     * @author machuanpeng
     */
    @Override
    public void addComment(XmlElement xmlElement) {
        List<Attribute> attributes = Optional.ofNullable(xmlElement.getAttributes()).orElse(new ArrayList<>());
        for (Attribute attribute : attributes) {
            if (Objects.equals(attribute.getName(), "id")) {
                String desc = this.getDesc(attribute.getValue());
                xmlElement.addElement(new TextElement("<!--" + desc + "-->"));
                break;
            }
        }
    }

    /**
     * 2019/3/10
     * 类属性注释
     *
     * @param field
     * @param introspectedTable
     * @return void
     * @author machuanpeng
     */
    @Override
    public void addFieldComment(Field field, IntrospectedTable introspectedTable) {
        field.addJavaDocLine("/**");
        String fieldName = field.getName();
        if (Objects.equals("orderByClause", fieldName)) {
            field.addJavaDocLine(" * 排序字段，直接通过传递字符串值指定.");
            field.addJavaDocLine(" * 如：index ASC,id DESC");
        } else if (Objects.equals("distinct", fieldName)) {
            field.addJavaDocLine(" * 是否指定distinct查询.");
        } else if (Objects.equals("oredCriteria", fieldName)) {
            field.addJavaDocLine(" * 拼装or条件.");
        } else if (Objects.equals("columnName", fieldName)) {
            field.addJavaDocLine(" * 数据库字段名.");
        } else if (Objects.equals("javaProperty", fieldName)) {
            field.addJavaDocLine(" * 实体属性名.");
        } else {
            field.addJavaDocLine(" * " + fieldName + ".");
        }
        field.addJavaDocLine(" */");
    }

    /**
     * 2018/7/20
     * 接口方法、xml描述
     *
     * @param methodName
     * @return java.lang.String
     * @author machuanpeng
     */
    private String getDesc(String methodName) {
        if (Objects.equals(methodName, "insert")) {
            return "保存记录";
        }
        if (Objects.equals(methodName, "insertSelective")) {
            return "保存记录（非空字段）";
        }
        if (Objects.equals(methodName, "deleteByPrimaryKey")) {
            return "根据主键删除记录";
        }
        if (Objects.equals(methodName, "updateByPrimaryKey")) {
            return "根据主键更新记录";
        }
        if (Objects.equals(methodName, "updateByPrimaryKeySelective")) {
            return "根据主键更新记录（非空字段）";
        }
        if (Objects.equals(methodName, "selectByPrimaryKey")) {
            return "根据主键查询记录";
        }
        if (Objects.equals(methodName, "deleteByExample")) {
            return "根据指定的条件删除数据库符合条件的记录";
        }
        if (Objects.equals(methodName, "updateByExample")) {
            return "根据指定的条件来更新符合条件的记录";
        }
        if (Objects.equals(methodName, "updateByExampleSelective")) {
            return "根据指定的条件来更新符合条件的记录（非空字段）";
        }
        if (Objects.equals(methodName, "selectByExample")) {
            return "根据指定的条件查询符合条件的记录";
        }
        if (Objects.equals(methodName, "countByExample")) {
            return ("根据指定的条件获取数据库记录数");
        }
        if (Objects.equals(methodName, "BaseResultMap")) {
            return "基本结果映射";
        }
        if (Objects.equals(methodName, "Example_Where_Clause")) {
            return "Example的Where条件";
        }
        if (Objects.equals(methodName, "Update_By_Example_Where_Clause")) {
            return "Example更新语句的Where条件";
        }
        if (Objects.equals(methodName, "Base_Column_List")) {
            return "表基础字段";
        }
        if (Objects.equals(methodName, "selectAll")) {
            return "查询全表记录";
        }
        if (Objects.equals(methodName, "selectOneByExample")) {
            return "根据指定的条件来查询符合条件的单条记录";
        }
        if (Objects.equals(methodName, "batchInsert")) {
            return "批量插入记录";
        }
        if (Objects.equals(methodName, "setOrderByClause")) {
            return ("设置排序字段");
        }
        if (Objects.equals(methodName, "setDistinct")) {
            return ("设置过滤重复数据");
        }
        if (Objects.equals(methodName, "getOredCriteria")) {
            return ("获取当前的查询条件实例");
        }
        if (Objects.equals(methodName, "isDistinct")) {
            return ("是否过滤重复数据");
        }
        if (Objects.equals(methodName, "getOrderByClause")) {
            return ("获取排序字段");
        }
        if (Objects.equals(methodName, "createCriteria")) {
            return ("创建一个查询条件");
        }
        if (Objects.equals(methodName, "createCriteriaInternal")) {
            return ("内部构建查询条件对象");
        }
        if (Objects.equals(methodName, "clear")) {
            return ("清除查询条件");
        }
        return methodName;
    }

    /**
     * 文件说明.
     * 2020/3/27
     *
     * @param compilationUnit compilationUnit
     * @return void
     * @author machuanpeng
     */
    @Override
    public void addJavaFileComment(CompilationUnit compilationUnit) {
        // add no file level comments by default
    }

    /**
     * xml根节点.
     * 2020/3/27
     *
     * @param rootElement rootElement
     * @return void
     * @author machuanpeng
     */
    @Override
    public void addRootComment(XmlElement rootElement) {
        // add no document level comments by default
    }

    @Override
    public void addClassComment(InnerClass innerClass, IntrospectedTable introspectedTable) {

    }

    /**
     * 内部类注释.
     * 2019/5/15
     *
     * @param innerClass        innerClass
     * @param introspectedTable introspectedTable
     * @param markAsDoNotDelete markAsDoNotDelete
     * @return void
     * @author machuanpeng
     */
    @Override
    public void addClassComment(InnerClass innerClass, IntrospectedTable introspectedTable, boolean markAsDoNotDelete) {

    }

    /**
     * 内部枚举类注释.
     * 2019/5/15
     *
     * @param innerEnum         innerEnum
     * @param introspectedTable introspectedTable
     * @return void
     * @author machuanpeng
     */
    @Override
    public void addEnumComment(InnerEnum innerEnum, IntrospectedTable introspectedTable) {

    }

    @Override
    public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable,
                                           Set<FullyQualifiedJavaType> imports) {
        imports.add(new FullyQualifiedJavaType("javax.annotation.Generated")); //$NON-NLS-1$
        String comment = "Source Table: " + introspectedTable.getFullyQualifiedTable().toString(); //$NON-NLS-1$
        method.addAnnotation(getGeneratedAnnotation(comment));
    }

    @Override
    public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable,
                                           IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {
        imports.add(new FullyQualifiedJavaType("javax.annotation.Generated")); //$NON-NLS-1$
        String comment = "Source field: " //$NON-NLS-1$
                + introspectedTable.getFullyQualifiedTable().toString()
                + "." //$NON-NLS-1$
                + introspectedColumn.getActualColumnName();
        method.addAnnotation(getGeneratedAnnotation(comment));
    }

    @Override
    public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable,
                                   Set<FullyQualifiedJavaType> imports) {
        imports.add(new FullyQualifiedJavaType("javax.annotation.Generated")); //$NON-NLS-1$
        String comment = "Source Table: " + introspectedTable.getFullyQualifiedTable().toString(); //$NON-NLS-1$
        field.addAnnotation(getGeneratedAnnotation(comment));
    }

    @Override
    public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable,
                                   IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {
        imports.add(new FullyQualifiedJavaType("javax.annotation.Generated")); //$NON-NLS-1$
        String comment = "Source field: " //$NON-NLS-1$
                + introspectedTable.getFullyQualifiedTable().toString()
                + "." //$NON-NLS-1$
                + introspectedColumn.getActualColumnName();
        field.addAnnotation(getGeneratedAnnotation(comment));

        if (!suppressAllComments && addRemarkComments) {
            String remarks = introspectedColumn.getRemarks();
            if (addRemarkComments && StringUtility.stringHasValue(remarks)) {
                field.addJavaDocLine("/**"); //$NON-NLS-1$
                field.addJavaDocLine(" * Database Column Remarks:"); //$NON-NLS-1$
                String[] remarkLines = remarks.split(System.getProperty("line.separator"));  //$NON-NLS-1$
                for (String remarkLine : remarkLines) {
                    field.addJavaDocLine(" *   " + remarkLine);  //$NON-NLS-1$
                }
                field.addJavaDocLine(" */"); //$NON-NLS-1$
            }
        }
    }

    @Override
    public void addClassAnnotation(InnerClass innerClass, IntrospectedTable introspectedTable,
                                   Set<FullyQualifiedJavaType> imports) {
        imports.add(new FullyQualifiedJavaType("javax.annotation.Generated")); //$NON-NLS-1$
        String comment = "Source Table: " + introspectedTable.getFullyQualifiedTable().toString(); //$NON-NLS-1$
        innerClass.addAnnotation(getGeneratedAnnotation(comment));
    }

    private String getGeneratedAnnotation(String comment) {
        StringBuilder buffer = new StringBuilder();
        buffer.append("@Generated("); //$NON-NLS-1$
        if (suppressAllComments) {
            buffer.append('\"');
        } else {
            buffer.append("value=\""); //$NON-NLS-1$
        }

        buffer.append(MyBatisGenerator.class.getName());
        buffer.append('\"');

        if (!suppressDate && !suppressAllComments) {
            buffer.append(", date=\""); //$NON-NLS-1$
            buffer.append(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now()));
            buffer.append('\"');
        }

        if (!suppressAllComments) {
            buffer.append(", comments=\""); //$NON-NLS-1$
            buffer.append(comment);
            buffer.append('\"');
        }

        buffer.append(')');
        return buffer.toString();
    }

}
